#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

#define WIDTH           40
#define HEIGHT          20
#define MAX_SNAKE       (WIDTH * HEIGHT)
#define HIGHSCORE_FILE  "highscore.txt"

// 控制台颜色常量
enum {
    C_BORDER      = 9,   // 边框
    C_SNAKE_HEAD  = 14,  // 蛇头：亮黄
    C_SNAKE_BODY  = 10,  // 蛇身：亮绿
    C_FOOD        = 12,  // 食物：亮红
    C_TEXT        = 15,  // 文本：白
    C_TITLE       = 13   // 标题：亮紫
};

typedef struct { int x, y; } Point;

// 全局状态
static Point  snake[MAX_SNAKE];
static int    snake_len;
static Point  food;
static int    direction;     // 0→ 1↓ 2← 3↑
static bool   gameOver;
static int    score;
static int    speed;
static int    highscore;
static const int defaultSpeed = 120;

// ASCII/Unicode 蛇艺术
static const char *snakeArt[] = {
"      _    _\n",
"    -(|)--(|)-.\n",
"   \\_   ..   _/\n",
"    \\______/\n",
"       V  V                                     ____\n",
"      `.^^`.                               /^,--`\n",
"      \\^^^\\                             (^^\\ \n",
"       |^^^|                  _,-._       \\^^\\ \n",
"      (^^^^\\      __      _,-'^^^^^`.    _,'^^)\n",
"      \\^^^^`._,-'^^`-._.'^^^^__^^^^ `--'^^^_/\n",
"       \\^^^^^ ^^^_^^^^^^^_,-'  `.^^^^^^^^_/ \n",
"         `.____,-' `-.__.'        `-.___.'   `\n",

};
static const int snakeArtLines = sizeof(snakeArt) / sizeof(snakeArt[0]);

// —— 工具函数 ——

// 隐藏光标 & UTF-8
void InitConsole() {
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);
    CONSOLE_CURSOR_INFO ci = {1, FALSE};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ci);
}

// 设置文字颜色
void SetColor(int c) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
}

// 定位光标
void Gotoxy(int x, int y) {
    COORD pos = {(SHORT)x, (SHORT)y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

// 清屏
void ClearScreen() {
    system("cls");
}

// 读取历史最高分
void LoadHighScore() {
    FILE *fp = fopen(HIGHSCORE_FILE, "r");
    if (fp) {
        fscanf(fp, "%d", &highscore);
        fclose(fp);
    } else {
        highscore = 0;
    }
}

// 保存最高分
void SaveHighScore() {
    if (score > highscore) {
        FILE *fp = fopen(HIGHSCORE_FILE, "w");
        if (fp) {
            fprintf(fp, "%d", score);
            fclose(fp);
        }
    }
}

// 绘制边框（只调用一次）
void DrawBorder() {
    SetColor(C_BORDER);
    Gotoxy(0, 0);      printf("╔");
    for (int x = 1; x < WIDTH; x++) printf("═");
    printf("╗");
    for (int y = 1; y < HEIGHT; y++) {
        Gotoxy(0, y);  printf("║");
        Gotoxy(WIDTH, y); printf("║");
    }
    Gotoxy(0, HEIGHT); printf("╚");
    for (int x = 1; x < WIDTH; x++) printf("═");
    printf("╝");
}

// 随机生成食物
void GenerateFood() {
    do {
        food.x = rand() % (WIDTH - 1) + 1;
        food.y = rand() % (HEIGHT - 1) + 1;
        bool overlap = false;
        for (int i = 0; i < snake_len; i++) {
            if (snake[i].x == food.x && snake[i].y == food.y) {
                overlap = true; break;
            }
        }
        if (!overlap) break;
    } while (1);
    SetColor(C_FOOD);
    Gotoxy(food.x, food.y);
    printf("★");
}

// 显示分数和最高分
void DrawScore() {
    SetColor(C_TEXT);
    Gotoxy(WIDTH + 2, 1); printf("分数 : %3d", score);
    Gotoxy(WIDTH + 2, 2); printf("最高 : %3d", highscore);
    Gotoxy(WIDTH + 2, 3); printf("速率 : %3dms", speed);
}

// 打印字符蛇艺术，startX/Y 为左上起点
void printsnake(int startX, int startY) {
    // 蛇身颜色
    SetColor(C_SNAKE_BODY);
    for (int i = 0; i < snakeArtLines; i++) {
        Gotoxy(startX, startY + i);
        printf("%s", snakeArt[i]);
    }
    // 找到并绘制蛇头 'O'
    int headLine = 1;
    const char *p = strchr(snakeArt[headLine], 'O');
    if (p) {
        int headCol = p - snakeArt[headLine];
        SetColor(C_SNAKE_HEAD);
        Gotoxy(startX + headCol, startY + headLine);
        putchar('O');
    }
    SetColor(C_TEXT);
}

// 初始化游戏状态及首次绘制
void InitGame() {
    LoadHighScore();
    snake_len = 4;
    int midx = WIDTH / 2, midy = HEIGHT / 2;
    for (int i = 0; i < snake_len; i++) {
        snake[i].x = midx - i;
        snake[i].y = midy;
    }
    direction = 0;
    score     = 0;
    speed     = defaultSpeed;
    gameOver  = false;

    ClearScreen();
    DrawBorder();
    DrawScore();

    // 初次绘制蛇身+蛇头
    for (int i = 0; i < snake_len; i++) {
        SetColor(i == 0 ? C_SNAKE_HEAD : C_SNAKE_BODY);
        Gotoxy(snake[i].x, snake[i].y);
        printf(i == 0 ? "●" : "■");
    }

    GenerateFood();
}

// 处理输入与速度控制
void ProcessInput() {
    // 方向键
    if (_kbhit()) {
        int ch = _getch();
        if (ch == 0 || ch == 0xE0) {
            switch (_getch()) {
                case 72: if (direction != 1) direction = 3; break;
                case 80: if (direction != 3) direction = 1; break;
                case 75: if (direction != 0) direction = 2; break;
                case 77: if (direction != 2) direction = 0; break;
            }
        } else if (ch == 27) {
            gameOver = true;
        }
    }
    // 长按 Shift 减速
    if (GetAsyncKeyState(VK_SHIFT) & 0x8000) {
        speed = defaultSpeed + 80;
    }
    // Caps Lock 加速
    else if (GetKeyState(VK_CAPITAL) & 0x0001) {
        speed = max(10, defaultSpeed - 60);
    }
    else {
        speed = defaultSpeed;
    }
}

// 更新蛇身，检测碰撞并吃食物
void UpdateSnake() {
    Point head = snake[0];
    switch (direction) {
        case 0: head.x++; break;
        case 1: head.y++; break;
        case 2: head.x--; break;
        case 3: head.y--; break;
    }
    // 撞墙或自咬
    if (head.x <= 0 || head.x >= WIDTH || head.y <= 0 || head.y >= HEIGHT) {
        gameOver = true; return;
    }
    for (int i = 0; i < snake_len; i++) {
        if (snake[i].x == head.x && snake[i].y == head.y) {
            gameOver = true; return;
        }
    }
    // 绘制新头
    SetColor(C_SNAKE_HEAD);
    Gotoxy(head.x, head.y);
    printf("●");
    // 身体后移
    for (int i = snake_len; i > 0; i--) {
        snake[i] = snake[i - 1];
    }
    snake[0] = head;
    // 吃食物
    if (head.x == food.x && head.y == food.y) {
        snake_len++;
        score += 10;
        DrawScore();
        GenerateFood();
    } else {
        // 擦尾
        Point tail = snake[snake_len];
        Gotoxy(tail.x, tail.y);
        printf(" ");
    }
    // 重绘前一个头为身体
    SetColor(C_SNAKE_BODY);
    Gotoxy(snake[1].x, snake[1].y);
    printf("■");
}

// 游戏主循环
void GameLoop() {
    InitGame();
    while (!gameOver) {
        ProcessInput();
        UpdateSnake();
        Sleep(speed);
    }
    SaveHighScore();
}

// 美化欢迎界面
void ShowWelcomeScreen() {
    ClearScreen();
    SetColor(C_TITLE);
    Gotoxy(5, 2);  printf("╔════════════════════════════════╗");
    Gotoxy(5, 3);  printf("║        经典 贪 吃 蛇        ║");
    Gotoxy(5, 4);  printf("╠════════════════════════════════╣");
    // 在标题下方添加最高分显示
    Gotoxy(5, 5);  printf("║        历史最高分: %-3d        ║", highscore);  // 新增行
    Gotoxy(5, 6);  printf("╚════════════════════════════════╝");  // 修改位置
    
    // 绘制字符蛇艺术（位置下移一行）
    printsnake(10, 7);  // 从原来的y=6改为y=7
    
    // 菜单（位置下移一行）
    SetColor(C_TEXT);
    Gotoxy(12, 20); printf("[1] 开始游戏");  // 19->20
    Gotoxy(12, 21); printf("[2] 游戏说明");  // 20->21
    Gotoxy(12, 22); printf("[3] 退出游戏");  // 21->22
    SetColor(C_TITLE);
    Gotoxy(5, 26);  printf("╚════════════════════════════════╝");  // 25->26
}

// 说明界面
void ShowInstructions() {
    ClearScreen();
    SetColor(C_TITLE);
    Gotoxy(6, 4);  printf("==== 操作说明 ====");
    SetColor(C_TEXT);
    Gotoxy(4, 7);  printf("方向键 ↑↓←→ 控制移动");
    Gotoxy(4, 8);  printf("Shift：减速（长按）");
    Gotoxy(4, 9);  printf("Caps Lock：加速（开关）");
    Gotoxy(4,10);  printf("ESC：退出   吃食物+10分");
    Gotoxy(4,12);  printf("撞墙或咬到自己则游戏结束");
    Gotoxy(4,14);  printf("按任意键返回菜单");
    _getch();
}

// 游戏结束界面
void ShowGameOver() {
    ClearScreen();
    SetColor(C_BORDER);
    Gotoxy(12, 8);  printf("████████ 游戏结束 ████████");
    SetColor(C_TEXT);
    Gotoxy(12,10);  printf("本局得分 : %d", score);
    Gotoxy(12,11);  printf("最高分数 : %d", highscore);
    Gotoxy(12,13);  printf("按任意键返回菜单");
    _getch();
}

// 主入口
int main() {
    InitConsole();
    while (true) {
        ShowWelcomeScreen();
        char c = _getch();
        if (c == '1') {
            GameLoop();
            ShowGameOver();
        }
        else if (c == '2') {
            ShowInstructions();
        }
        else if (c == '3' || c == 'q' || c == 'Q') {
            break;
        }
    }
    return 0;
}
